/*

Copyright 2011 James Frank

This file is part of JATH.

JATH is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

function storage_check() {
	try {
//		return false; // uncomment to check without localStorage
		return 'localStorage' in window && window['localStorage'] !== null;
	} catch (e) {
		return false;
	}
}

supports_storage = storage_check();

// find our accessibility errors
function runJath() {
	
	// if we're in a modern browser, let us store ignore flags
	if (supports_storage) {
		// improve localStorage to handle objects
		Storage.prototype.setObject = function(key, value) {
		    this.setItem(key, JSON.stringify(value));
		}
		Storage.prototype.getObject = function(key) {
		    return JSON.parse(this.getItem(key));
		}
		// This code is to reset storage
//		delete localStorage.accessibilityChecker;
		if (!localStorage.hasOwnProperty('accessibilityChecker')) {
			localStorage.setObject('accessibilityChecker',new Object);
		}
		aStorage = localStorage.getObject('accessibilityChecker');
		if (!aStorage.hasOwnProperty(document.location.pathname)) {
			aStorage[document.location.pathname] = new Object;
			aStorage[document.location.pathname].path = document.location.pathname;
			aStorage[document.location.pathname].elements = new Array;
		}
		pagePrefs = aStorage[document.location.pathname];
	}

	// set the error object
	var errors = new Array;
	// do we skip anything?
	if (typeof accessibilityErrorsToSkip == 'undefined' ) accessibilityErrorsToSkip = new Array;
	
	// run a test on the elements and add to our error object
	function createTest(hover,message,elements,ignore) {
		if (elements.length > 0 && $.inArray(ignore,accessibilityErrorsToSkip) == -1) {
			elements = elements.filter(function(){
				// make sure we're not supposed to be ignoring this one
				if (supports_storage && $.inArray($(this).attr('id'),pagePrefs.elements) != -1) return false;
				return true;
			});
			elements.each(function(i,val){
				if ($(this).data('jathErrors') == undefined) {
					$(this).data('jathErrors',[hover])
				} else {
					var elementErrors = $(this).data('jathErrors');
					elementErrors.push(hover);
					$(this).data('jathErrors',elementErrors);	
				}
			});
			tempError = new Object
			tempError.instances = elements;
			tempError.message = message;
			tempError.hover = hover;
			errors.push(tempError);
		}
	}
	
	// check inputs missingLabels
	createTest(	'This input has no associated label and no title attribute.',
				'There are inputs without an associated label or a title attribute.',
				$('input:not([type="submit"],[type="hidden"],[type="button"],[type="image"]),textarea,select').filter(function(){
					if ($(this).attr('id').length == 0 || $('label[for="'+$(this).attr('id').replace(new RegExp(':', 'g'),'\\:')+'"]').length == 0 && $(this).attr('title').length == 0) {
						return true;
					} else { return false; }
				}),
				'labels'
	);	
		
	// check radios inFieldsets
	createTest(	'This input is not in a fieldset.',
				'There are radio buttons or checkboxes not in fieldsets.',
				$('input:[type="radio"]').filter(function(){
					if($(this).closest('fieldset').length == 0) return true;
				}),
				'fieldsets'
	);
			
	// check images missingAlts
	createTest(	'This image has no alt text.',
				'There are images without alternate text.',
				$('img:not([alt]),input[type=image]:not([alt])'),
				'alts'
	);
	
	// check tables for captions or summaries
	createTest(	'This table does not have a caption or summary.',
				'There are tables without captions or summaries.',
				$('table:not([summary])').filter(function(){
					if ($(this).children('caption').length == 0) return true;
				}),
				'captions'
	);
	
	// check table rows for table headers
	createTest(	'This table does not have appropriate table headers.',
				'There are tables without appropriate table headers.',
				$('table').filter(function(){
					// if there are no headers flag it
					if ($(this).filter('th').length === 0) return true;
					// if the whole first row is headers don't flag it
					if ($(this).find('tr:eq(0)').children('th').length > 0 && $(this).find('tr:eq(0)').children('td').length == 0) return false;
					// if each row starts with a header, don't flag it
					if ($(this).find('tr').filter(function(){
						if ($(this).children(':eq(0)').filter('th').length == 0) return true;
					}).length == 0) return false;
					// if the first row and column are headers except the top left cell, don't flag it
					if ($(this).find('tr:eq(0)').children(':eq(0)').filter('td').length > 0) {
						if ($(this).find('tr:gt(0)').filter(function(){
							if ($(this).children(':eq(0)').filter('th').length == 0) return true;
						}).length == 0) return false;
					}
					// else flag it
					return true;
				}),
				'headers'
	);
	
	// check legends on fieldsets
	createTest(	'This fieldset does not have a legend.',
				'There are fieldsets without legends.',
				$('fieldset').filter(function(){
					if ($(this).children('legend').length == 0) return true;
				}),
				'legends'
	);
	
	// check for spans with color and no bold or italics
	createTest(	'This element uses color to identify content without also using bold or italics.',
				'Color is used to identify content without also using bold or italitcs.',
				$('span').filter(function(){
					if ( 	$(this).attr('style') != undefined &&
							$(this).css('color') != $(this).parent().css('color') &&
							(	$(this).css('font-weight') == 'normal' && 
								$(this).css('font-style') == 'normal'
							)
						) 	{
								if ($(this).children('b,strong,i,em').length == 0) {
									return true;
								} else {
									return false;
								}
							} else {
								return false;
							}
				}),
				'color'
	);
	
	
	// do we have errors?
	// process and display them
	$.each(errors, function(i,val) {
		$.each(val.instances, function(i,val) {
			// identify the element
			if (!$(this).parent().hasClass('jath-parent')) $(this).wrap('<span class="jath-parent">');
			if ($(this).filter('[type="radio"],[type="checkbox"]').length > 0) $(this).parent().addClass('jath-parent-color');
			// on hover display a message
			$(this).parent().hover(function(e) {
				$('.jath-tooltip').remove();
				if ($(val).data('jathErrors').length > 1) {
					hover = "<ul>"
					for (error in $(val).data('jathErrors')) {
						hover = hover + "<li>" + $(val).data('jathErrors')[error] + "</li>";
					}
					hover = hover + "</ul>";
				} else {
					hover = $(val).data('jathErrors')[0];
				}
				$(this).append('<span class="jath-tooltip">'+hover+'</span>');
				// be able to set a flag to ignore an element for future checks
				if (supports_storage) {
					$(this).children('.jath-tooltip').append(' <button class="jath-ignore" '+(($(val).attr('id').length == 0) ? 'disabled="disabled"' : '') +'>Ignore</button>');
					$(this).find('.jath-ignore').click(function() {
						if (!($(val).attr('id') in pagePrefs.elements)) pagePrefs.elements.push($(val).attr('id'));
						localStorage.setObject('accessibilityChecker',aStorage);
						$(this).parent().remove(); // get rid of parent tooltip
						$(val).unwrap(); // unwrap the element in question
					});
				}
				// position the tooltip
				tooltip = $(this).find('.jath-tooltip');
				var top = (e.pageY-jQuery(window).scrollTop() < tooltip.outerHeight() ? e.pageY : e.pageY-tooltip.outerHeight());
				var left = (jQuery(window).width() - e.pageX-jQuery(window).scrollLeft() < tooltip.outerWidth() ? e.pageX-tooltip.outerWidth() : e.pageX);
				tooltip.css({
					top: top,
					left: left
				});	
			},
			function() {
				$('.jath-tooltip').remove();
			});	
		});	
	// TBD // other stuff
		$('#jath-holder ul').append('<li>'+val.message+'</li>');
	});
	if (errors.length > 0) {
		$('#jath-wrapper').css('background-color','red');
	} else {
		$('#jath-wrapper').css('background-color','green');
		$('#jath-error-list').append('<li>Congratulations, no errors!</li>');
	}
	
	return errors;
			
}

$(document).ready(function() {
	
	// create the holder
	$('body').append('\
		<div id="jath-wrapper">\
			<div id="jath-holder">\
				<button id="jath-clear">Clear Page</button>\
				<button id="jath-recheck">Recheck Page</button>\
				<h1>JATH Report</h1>\
				<ul id="jath-error-list"></ul>\
				<button id="jath-full-report">Full JATH Report</button>\
			</div>\
		</div>\
	');
	if (supports_storage) {;
		$('#jath-recheck').after('\
			<button id="jath-reset">Reset Ignored Elements</button>\
			<button id="jath-reset-site">Reset Site</button>\
		');	
	}
	// the trigger for our report
	$('#jath-wrapper').hover(function(){
		$('#jath-holder').slideToggle(50);	
	});
	// clear everything that jath does
	function clearJath() {
		$('#jath-error-list').empty();
		$('.jath-parent *:not(.jath-tooltip)').removeData('jathErrors');
		$('.jath-parent').children().unwrap();
	}
	$('#jath-clear').click(function(){
		clearJath();
		$('#jath-error-list').append('<li>You have cleared the page.</li>');
	});
	// recheck the page
	$('#jath-recheck').click(function(){
		clearJath();
		runJath();
	});
	// remove ignores for this page
	$('#jath-reset').click(function(){
		pagePrefs.elements = [];
		localStorage.setObject('accessibilityChecker',aStorage);
		clearJath();
		runJath();
	});
	// remove all ignores for entire site
	$('#jath-reset-site').click(function(){
		if(confirm('This will clear all ignored elements on the entire site. Are you sure you want to continue?')) {
			aStorage = new Object;
			localStorage.setObject('accessibilityChecker',aStorage);
			clearJath();
			runJath();
		}
	});
	// create and open full jath report
	$('#jath-full-report').click(function() {
		clearJath();
		errors = runJath();
		jathWindow = window.open('','jath-full-report-window','toolbar=no,statusbar=no,location=no,scrollbars=yes,resizable=yes,width=500,height=500');
		var jathErrorReport = "";
		var date = new Date;
		jathErrorReport += "<dl><dt>URL</dt><dd><a href='" + document.location + "'>" + document.location + "</a></dd><dt>Date</dt><dd>"+date+"</dd></dl>"
		if (pagePrefs.elements.length > 0) jathErrorReport += "<h2>Ignored Elements</h2><p>There are ignored elements on this page. Reset the ignored elements on the page and re-run JATH for a complete report.</p>"
		for (var error in errors) {
			jathErrorReport += '<h2>'+errors[error].message+'</h2>';
			$.each(errors[error].instances,function(i) {
				id = ($(this).attr('id').length > 0) ? $(this).attr('id') : "<em>null</em>";
				html = $(this).clone().wrap("<div></div>").parent().html();
				jathErrorReport += "<h3>Instance "+(i+1)+" - ID: "+id+"</h3><pre style='background-color: #eee; border: 1px dashed black; padding: 5px; word-wrap: break-word;'>"+html.replace(new RegExp('>', 'g'),'&gt;').replace(new RegExp('<', 'g'),'&lt;')+"</pre>";
			});
		}
		jathFullReport = "\
			<html>\
				<head>\
					<title>JATH Full Report</title>\
				</head>\
				<body>\
					<h1>JATH Full Report</h1>\
					"+jathErrorReport+"\
				</body>\
		";
		jathWindow.document.write(jathFullReport);
	});
	
	runJath();
	
});
